package at.fhooe.mc.duel_game;

import android.content.Context;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
import android.util.Log;
import android.widget.Toast;
import at.fhooe.mc.duel_game.activities.GameActivity;
import at.fhooe.mc.duel_game.activities.GameMenuActivity;
import at.fhooe.mc.duel_game.activities.ResultActivity;
import at.fhooe.mc.duel_game.bluetooth.BluetoothConnectionService;

public class GameLogic {

	private static GameLogic _instance;
	private Context context;
	private GameActivity gameActivity;
	private GameMenuActivity gameMenuActivity;
	private Handler handler;
	private GameState state = GameState.NONE;
	private boolean mIsServer = false;

	private String mConnectedDeviceName;
	private static BluetoothConnectionService mBluetoothConnectionService;

	// Current system time of the other player
	private long mOtherSyncTime;
	// Current system of me
	private long mCurrentSyncTime;
	// The timediff between the current system times
	private long mTimeDiffSystem;
	// The time between and a request and a response
	private long mTimeDiffBluetooth;

	private int mCountdownCounter;

	private long mTimeGunUnlocked;
	private long mTimeNeededToFire;
	private long mTimeOtherNeeded;

	// Debugging
	private static final String TAG = "GameLogic";
	private static final boolean D = true;

	// Training Mode
	private boolean isTraining = false;

	// players
	private final Player mCurrentPlayer;

	public void setTraining(boolean training) {
		isTraining = training;
	}

	private GameLogic() {
		mCurrentPlayer = new Player();

		mCurrentPlayer.setName(android.os.Build.MODEL);
	}

	public GameState getState() {
		return state;
	}

	public void setHandler(Handler _handler) {
		handler = _handler;
	}

	public void setGameActivity(GameActivity _gameActivity) {
		gameActivity = _gameActivity;
	}

	public void setGameMenuActivity(GameMenuActivity gameMenuActivity) {
		this.gameMenuActivity = gameMenuActivity;
	}

	public void setState(GameState state) {
		this.state = state;
	}

	static synchronized public GameLogic getInstance() {
		if (_instance == null) {
			_instance = new GameLogic();
		}
		return _instance;
	}

	public void startReload() {

		setState(GameState.RELOAD_START);

		Runnable runnable = new Runnable() {

			public void run() {
				handler.post(new Runnable() {

					public void run() {
						gameActivity.updateTextView(context.getResources()
								.getString(R.string.cmdReload));
					}
				});
			}
		};
		new Thread(runnable).start();
	}

	public void reloadUp() {

		Runnable runnable = new Runnable() {

			public void run() {
				SoundManager.playSound(SoundManager.SOUND_RELOAD_1);
				Vibrator v = (Vibrator) context.getApplicationContext()
						.getSystemService(Context.VIBRATOR_SERVICE);
				long[] vSequence = new long[] { 1, 100 };
				v.vibrate(vSequence, -1);
			}
		};
		new Thread(runnable).start();

		setState(GameState.RELOAD_DOWN);
	}

	public void reloadDown() {

		Runnable runnable = new Runnable() {

			public void run() {
				Vibrator v = (Vibrator) context.getApplicationContext()
						.getSystemService(Context.VIBRATOR_SERVICE);
				long[] vSequence = new long[] { 1, 100 };
				v.vibrate(vSequence, -1);
				SoundManager.playSound(SoundManager.SOUND_RELOAD_2);

				handler.post(new Runnable() {

					public void run() {
						gameActivity.updateTextView(context.getResources()
								.getString(R.string.cmdGetReady));
					}
				});
			}
		};
		new Thread(runnable).start();

		setState(GameState.FIRE_BEFORE_READY_STATE);
	}

	public void ready() {

		Runnable runnable = new Runnable() {

			public void run() {

				Vibrator v = (Vibrator) context.getApplicationContext()
						.getSystemService(Context.VIBRATOR_SERVICE);
				long[] vSequence = new long[] { 1, 100 };
				v.vibrate(vSequence, -1);

				handler.post(new Runnable() {

					public void run() {
						gameActivity.updateTextView(context.getResources()
								.getString(R.string.cmdWaitForOpponent));
					}
				});
			}
		};

		new Thread(runnable).start();

		setState(GameState.READY_TO_FIRE);

		if (!isTraining) {
			sendCmd(new DuelCommand(Commands.READY_TO_FIRE));
		} else {
			startCountdown(5000, 1000);

		}
	}

	public void unlockGun() {

		Runnable runnable = new Runnable() {

			public void run() {

				Vibrator v = (Vibrator) context.getApplicationContext()
						.getSystemService(Context.VIBRATOR_SERVICE);
				long[] vSequence = new long[] { 1, 100 };
				v.vibrate(vSequence, -1);

				handler.post(new Runnable() {

					public void run() {
						gameActivity.updateTextView(context.getResources()
								.getString(R.string.cmdFire));
					}
				});

			}
		};

		new Thread(runnable).start();

		mTimeGunUnlocked = System.currentTimeMillis();
		mTimeOtherNeeded = -1;
		mTimeNeededToFire = -1;

		setState(GameState.FIRE_READY_STATE);
	}

	public void fire() {

		Runnable runnable = new Runnable() {

			public void run() {
				Vibrator v = (Vibrator) context
						.getSystemService(Context.VIBRATOR_SERVICE);
				long[] vSequence = new long[] { 1, 100 };
				v.vibrate(vSequence, -1);
				SoundManager.playSound(SoundManager.SOUND_GUNFIRE);

				handler.post(new Runnable() {

					public void run() {

					}
				});
			}
		};
		new Thread(runnable).start();

		mTimeNeededToFire = System.currentTimeMillis() - mTimeGunUnlocked;

		if (!isTraining) {
			sendCmd(new DuelCommand(Commands.TIME_NEEDED, mTimeNeededToFire));

			if (mTimeOtherNeeded >= 0) {
				showResult(mTimeNeededToFire, mTimeOtherNeeded);
			}
		} else {
			showResult(mTimeNeededToFire, Long.MAX_VALUE);
		}

		startReload();
	}

	public void startServer() {
		setState(GameState.WAIT_FOR_CLIENTS);
		mIsServer = true;
	}

	public void connectToServer() {
		setState(GameState.CONNECT_TO_SERVER);
		mIsServer = false;
	}

	public boolean isServer() {
		return mIsServer;
	}

	private void printCmd(DuelCommand cmd) {
		Log.i(TAG, "CMD = " + cmd.getCommand() + ", Data = " + cmd.GetStrData());
	}

	private void sendCmd(DuelCommand cmd) {
		// Check that we're actually connected before trying anything
		if (mBluetoothConnectionService.getState() != BluetoothConnectionService.STATE_CONNECTED) {
			Toast.makeText(context, R.string.titleNotConnected,
					Toast.LENGTH_SHORT).show();
			return;
		}

		mBluetoothConnectionService.write(cmd);
	}

	public void initBluetoothConnectionService(Context context) {

		this.context = context;

		if (mBluetoothConnectionService == null)
			mBluetoothConnectionService = new BluetoothConnectionService(
					context, mHandler);
	}

	public BluetoothConnectionService getBluetoothService() {
		return mBluetoothConnectionService;
	}

	private void showGameDialog() {
		if (gameMenuActivity != null) {
			Intent intent = new Intent(context, GameActivity.class);
			intent.putExtra("TRAINING", false);
			gameMenuActivity.startActivity(intent);
		}
	}

	public void startSyncReq() {
		setState(GameState.SYNC_GAME);
		mCurrentSyncTime = System.currentTimeMillis();
		sendCmd(new DuelCommand(Commands.SYNC_REQ, mCurrentSyncTime));
	}

	private void handleCmd(DuelCommand cmd) {
		if (D)
			Log.d(TAG, cmd.getCommand().toString());

		switch (cmd.getCommand()) {
		// Sync-request from server
		case SYNC_REQ:
			mCurrentSyncTime = System.currentTimeMillis();
			mOtherSyncTime = ByteConverters.makeLongFromByte8(cmd.GetData());
			mTimeDiffSystem = mCurrentSyncTime - mOtherSyncTime;

			sendCmd(new DuelCommand(Commands.SYNC_RSP, mCurrentSyncTime));

			break;

		// sync-response from client
		case SYNC_RSP:
			setState(GameState.WAIT_FOR_START);

			mOtherSyncTime = ByteConverters.makeLongFromByte8(cmd.GetData());
			mTimeDiffSystem = mCurrentSyncTime - mOtherSyncTime;
			mTimeDiffBluetooth = System.currentTimeMillis() - mCurrentSyncTime;
			mTimeDiffSystem += mTimeDiffBluetooth / 2;

			Log.d(TAG, "Time diff: " + String.valueOf(mTimeDiffBluetooth));

			sendCmd(new DuelCommand(Commands.SYNC_RSP_SERVER));

			startReload();

			break;

		case SYNC_RSP_SERVER:

			setState(GameState.WAIT_FOR_START);

			mTimeDiffBluetooth = System.currentTimeMillis() - mCurrentSyncTime;
			mTimeDiffSystem += mTimeDiffBluetooth / 2;

			Log.d(TAG, "Time diff: " + String.valueOf(mTimeDiffBluetooth));

			startReload();

			break;

		case READY_TO_FIRE:
			if (getState() == GameState.READY_TO_FIRE) {
				long otherStartTime = System.currentTimeMillis()
						- mTimeDiffSystem;

				sendCmd(new DuelCommand(Commands.START_COUNTDOWN,
						otherStartTime));

				startCountdownDelayed(1000, 5000, 1000);
			}

			break;

		case START_COUNTDOWN:
			long startTime = ByteConverters.makeLongFromByte8(cmd.GetData());
			startCountdownDelayed(
					(int) (startTime - System.currentTimeMillis()) + 1000,
					5000, 1000);
			break;

		case TIME_NEEDED:
			mTimeOtherNeeded = ByteConverters.makeLongFromByte8(cmd.GetData());

			if (mTimeNeededToFire > 0)
				showResult(mTimeNeededToFire, mTimeOtherNeeded);

			break;

		default:
			Toast.makeText(context, "Unkown cmd", Toast.LENGTH_SHORT).show();
			break;
		}
	}

	private void showResult(long mytime, long otherTime) {

		DuelResult result = new DuelResult(mCurrentPlayer.getName(), mytime,
				otherTime);

		Intent intent = new Intent(context, ResultActivity.class);
		intent.putExtra("RESULT", result);

		gameActivity.startActivity(intent);
	}

	// The Handler that gets information back from the
	// BluetoothConnectionService
	private final Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case BluetoothConnectionService.MESSAGE_STATE_CHANGE:
				if (D)
					Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
				switch (msg.arg1) {
				case BluetoothConnectionService.STATE_CONNECTED:
					GuiHelpers.closeProcessDialog();
					showGameDialog();
					break;
				}
				break;

			case BluetoothConnectionService.MESSAGE_READ:
				byte[] readBuf = (byte[]) msg.obj;

				DuelCommand cmd = DuelCommand.parseData(readBuf);

				handleCmd(cmd);
				printCmd(cmd);

				break;

			case BluetoothConnectionService.MESSAGE_DEVICE_NAME:
				// save the connected
				// device's name
				mConnectedDeviceName = msg.getData().getString(
						BluetoothConnectionService.DEVICE_NAME);
				Toast.makeText(
						context,
						context.getResources().getString(
								R.string.titleConnectedTo)
								+ " " + mConnectedDeviceName,
						Toast.LENGTH_SHORT).show();
				break;

			case BluetoothConnectionService.MESSAGE_TOAST:
				Toast.makeText(
						context,
						msg.getData().getString(
								BluetoothConnectionService.TOAST),
						Toast.LENGTH_SHORT).show();
				break;
			}
		}
	};

	private void startCountdown(int time, int period) {
		mCountdownCounter = 5;;

		SoundManager.playSound(SoundManager.SOUND_COUNTDOWN);
		
		gameActivity.updateTextView(String.valueOf(mCountdownCounter));

		CountDownTimer timer = new CountDownTimer(time, period) {

			@Override
			public void onTick(long millisUntilFinished) {

				if (D)
					Log.d(TAG, String.valueOf(millisUntilFinished));

				mCountdownCounter--;
				gameActivity.updateTextView(String.valueOf(mCountdownCounter));
			}

			@Override
			public void onFinish() {
				gameActivity.updateTextView(String.valueOf(mCountdownCounter));
				unlockGun();
			}
		};

		timer.start();
	}

	private void startCountdownDelayed(int waitTime, final int time,
			final int period) {

		//if (D)
			//Toast.makeText(context, "start countdown in " + waitTime + " ms",
				//	Toast.LENGTH_SHORT).show();

		final Handler handler = new Handler();
		handler.postDelayed(new Runnable() {

			public void run() {
				startCountdown(time, period);

			}
		}, waitTime);
	}
}
